<template>
  <div class="swipe" :style="{ height: props.height + 'px' }" ref="swipe">
    <div class="swipe_track" :style="trackStyle" ref="swipeTrack">
      <slot></slot>
    </div>

    <!-- 指示器 -->
    <div class="swipe_indicators" v-if="showIndicators && realItemCount">
      <span v-for="(_, index) in realItemCount" :key="index" class="indicator"
        :class="{ active: index === currentIndex }"></span>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick, computed } from 'vue';

const props = defineProps({
  height: {
    type: [Number, String],
    default: 200
  },
  showIndicators: {
    type: Boolean,
    default: true
  },
  duration: {
    type: Number,
    default: 300
  }
});

const emit = defineEmits(['change']);

// refs
const swipe = ref(null);
const swipeTrack = ref(null);

// 状态
const transitionDuration = ref('0ms');
const translateY = ref(0);
const currentIndex = ref(0);
const realItemCount = ref(0);
const itemHeight = ref(0);
const isTransitioning = ref(false);

// 手势
const startY = ref(0);
const moveY = ref(0);

// 样式
const trackStyle = computed(() => ({
  transitionDuration: transitionDuration.value,
  transform: `translateY(-${translateY.value}px)`
}));

// 生命周期
onMounted(() => {
  if (swipe.value) {
    swipe.value.addEventListener('touchstart', handleTouchStart, { passive: true });
    swipe.value.addEventListener('touchmove', handleTouchMove, { passive: false });
    swipe.value.addEventListener('touchend', handleTouchEnd, { passive: true });
  }
  initSwipe();
});

onBeforeUnmount(() => {
  if (swipe.value) {
    swipe.value.removeEventListener('touchstart', handleTouchStart, { passive: true });
    swipe.value.removeEventListener('touchmove', handleTouchMove, { passive: false });
    swipe.value.removeEventListener('touchend', handleTouchEnd, { passive: true });
  }
});

// 初始化
const initSwipe = () => {
  nextTick(() => {
    if (!swipeTrack.value) return;

    const children = Array.from(swipeTrack.value.children);
    realItemCount.value = children.length;

    if (realItemCount.value === 0) return;

    itemHeight.value = typeof props.height === 'number' ? props.height : parseInt(props.height, 10);
    resetPosition();
  });
};

const resetPosition = () => {
  transitionDuration.value = '0ms';
  translateY.value = 0;
  currentIndex.value = 0;
};

// 手势处理
const handleTouchStart = (e) => {
  if (realItemCount.value <= 1 || isTransitioning.value) return;
  startY.value = e.touches[0].clientY;
  moveY.value = 0;
};

const handleTouchMove = (e) => {
  if (realItemCount.value <= 1 || isTransitioning.value) return;

  moveY.value = e.touches[0].clientY - startY.value;
  if (Math.abs(moveY.value) > 10 && e.cancelable) {
    e.preventDefault();
  }

  // 实时跟随手指，但限制边界
  const targetY = currentIndex.value * itemHeight.value - moveY.value;
  const min = 0;
  const max = (realItemCount.value - 1) * itemHeight.value;
  translateY.value = Math.max(min, Math.min(targetY, max));
};

const handleTouchEnd = () => {
  if (realItemCount.value <= 1 || isTransitioning.value) return;

  const threshold = 50;
  isTransitioning.value = true;

  if (moveY.value > threshold && currentIndex.value > 0) {
    // 下滑：回到上一项
    currentIndex.value--;
  } else if (moveY.value < -threshold && currentIndex.value < realItemCount.value - 1) {
    // 上滑：切换到下一项
    currentIndex.value++;
  }

  // 触发位移动画
  transitionDuration.value = `${props.duration}ms`;
  translateY.value = currentIndex.value * itemHeight.value;

  setTimeout(() => {
    isTransitioning.value = false;
    emit('change', currentIndex.value);
  }, props.duration);

  moveY.value = 0;
};

// 跳转方法（供外部调用）
const goTo = (index, noTransition = false) => {
  if (index < 0 || index >= realItemCount.value || isTransitioning.value) return;

  isTransitioning.value = true;
  currentIndex.value = index;
  transitionDuration.value = noTransition ? '0ms' : `${props.duration}ms`;
  translateY.value = index * itemHeight.value;

  setTimeout(() => {
    isTransitioning.value = false;
    emit('change', index);
  }, noTransition ? 0 : props.duration);
};

const next = () => {
  if (currentIndex.value < realItemCount.value - 1) {
    goTo(currentIndex.value + 1);
  }
};

const prev = () => {
  if (currentIndex.value > 0) {
    goTo(currentIndex.value - 1);
  }
};

defineExpose({ next, prev, goTo });
</script>

<style lang="scss" scoped>
.swipe {
  position: relative;
  overflow: hidden;
  transform: translateZ(0);
  user-select: none;
  touch-action: pan-y;

  .swipe_track {
    width: 100%;
    display: flex;
    flex-direction: column;
    height: 100%;
  }

  .swipe_indicators {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    gap: 6px;

    .indicator {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background-color: rgba(255, 255, 255, 0.5);
      transition: background-color 0.3s;

      &.active {
        background-color: #fff;
      }
    }
  }
}
</style>